package com.example.touch;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;

import org.apache.http.client.ClientProtocolException;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Point;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdManager.ResolveListener;
import android.net.nsd.NsdServiceInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import com.example.touch.communication.WebServiceConnection;
import com.example.touch.communication.entities.ContextAwareModel;
import com.example.touch.communication.entities.IndoorPositionMap;
import com.example.touch.domain.EventActionRule;
import com.example.touch.mediator.EventActionAssembler;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class TouchImageViewActivity extends Activity {
	private NsdManager mNsdManager;
	private PositionListener positionListener;
	private WifiReceiver receiverWifi;
	private TouchImageView img;
	private ProgressDialog _waitDialog;
	private InetAddress m_Host;
	private int m_Port;
	private Point lastPosition;
	 private static List<BroadcastReceiver> receivers = new ArrayList<BroadcastReceiver>();  

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//startZeroConf();
		setContentView(R.layout.main);
		img = (TouchImageView) findViewById(R.id.img);
		img.setDrawableID(R.drawable.tek_kort_e);
		img.setMaxZoom(4);
		Point point = new Point(187, 19);
		img.setPoint(point);

		//startService(new Intent(this, NotificationService.class));

		 //WifiTester wifiTester = new WifiTester();
		 //wifiTester.testOnlineData();
		 //wifiTester.startTestNN();
		 //wifiTester.startTestKNN(5);
		 //wifiTester.startTestKNNWeighted(5);
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		if(isReceiverRegistered(positionListener) && isReceiverRegistered(receiverWifi))
        {
        	// toggle disable periodic position inference
        	unregisterReceiver(positionListener);
    		unregisterReceiver(receiverWifi);
    		
    		receivers.remove(positionListener);
    		receivers.remove(receiverWifi);
        }
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		receiverWifi = new WifiReceiver(this);
		positionListener = new PositionListener();
		receiverWifi.setOutlierThreshold(200);
		receiverWifi.setAverageWindowsSize(5);
		receiverWifi.setScanTimer(1000);
		
		togglePositionInference();
		
		//registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
		//registerReceiver(positionListener, new IntentFilter("position"));

		ContextAwareModel model = null;
		try {
			String xml = readFile("/IndoorPositioning/XML");
			Serializer serializer = new Persister();
			model = serializer.read(ContextAwareModel.class, xml);
		} catch (Exception e) {
			e.printStackTrace();
		}

		EventActionAssembler eaAssembler = new EventActionAssembler(this, model);
		Collection<EventActionRule> resolvedEARules = eaAssembler.resolveSemanticModel();
		EventActionRegistry.registerAll(resolvedEARules);

		
		
	}

	private void startZeroConf() {
		mNsdManager = (NsdManager) getSystemService(NSD_SERVICE);

		_waitDialog = new ProgressDialog(this);
		_waitDialog.setMessage("Looking for location services...");
		_waitDialog.setIndeterminate(true);
		_waitDialog.setCancelable(false);
		_waitDialog.show();

		initializeDiscoveryListener();
		initializeResolveListener();
		mNsdManager.discoverServices("_http._tcp", NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
	}

	private void OnDataLoaded() {

		Calendar c = Calendar.getInstance();
		Point point = new Point(0,0);
		EventActionRegistry.triggerActions(point, c);
	}

	private class PositionListener extends BroadcastReceiver {
		@Override
		public void onReceive(Context arg0, Intent arg1) {
			lastPosition = new Point(arg1.getIntExtra("x", 0), arg1.getIntExtra("y", 0));
			Log.i("POINT", "Point: " + lastPosition.x + " , " + lastPosition.y);
			if (lastPosition.x != 0 && lastPosition.y != 0) {
				img.setPoint(lastPosition);
				img.invalidate();
				Calendar c = Calendar.getInstance();
				EventActionRegistry.triggerActions(lastPosition, c);
			}
		}
	}
	
	public void onUpdatePosButtonClicked(View v)
    {
        EditText editTextX = (EditText) findViewById(R.id.editTextX);
        EditText editTextY = (EditText) findViewById(R.id.editTextY);
        
        String newXPositionString = editTextX.getText().toString();
        String newYPositionString = editTextY.getText().toString();
        
        int newXPosVal = Integer.parseInt(newXPositionString);
        int newYPosVal = Integer.parseInt(newYPositionString);
        
        lastPosition = new Point(newXPosVal, newYPosVal); 
        img.setPoint(lastPosition);
		img.invalidate();
		
		Calendar c = Calendar.getInstance();
        EventActionRegistry.triggerActions(lastPosition, c);
    }
	
	public void onEnablePosInferenceClicked(View v)
    {
		togglePositionInference();
    }
	
	private void togglePositionInference()
	{
		 CheckBox checkBox = (CheckBox) findViewById(R.id.enablePosInferenceChkBox);
	        
	        if(checkBox.isChecked())
	        {
	        	// toggle enable periodic position inference
	    		registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
	    		registerReceiver(positionListener, new IntentFilter("position"));
	    		
	    		receivers.add(positionListener);
	    		receivers.add(receiverWifi);
	        }
	        else if(isReceiverRegistered(positionListener) && isReceiverRegistered(receiverWifi))
	        {
	        	// toggle disable periodic position inference
	        	unregisterReceiver(positionListener);
	    		unregisterReceiver(receiverWifi);
	    		
	    		receivers.remove(positionListener);
	    		receivers.remove(receiverWifi);
	        }
	}
	
	public boolean isReceiverRegistered(BroadcastReceiver receiver){
        boolean registered = receivers.contains(receiver);
        Log.i(getClass().getSimpleName(), "is receiver "+receiver+" registered? "+registered);
        return registered;
    }

	private static String WEBSERVICE_TAG = "WebServiceConnection";

	public void InitializeWebServiceData() {

		String address = "http://" + m_Host.getHostAddress().replace("/", "") + ":" + m_Port + "/"
				+ "MapService.svc/Map/";

		Log.i(WEBSERVICE_TAG, "Retrieving data from: " + address);

		WebServiceConnection webservice = new WebServiceConnection(address);
		try {
			String map = webservice.getMap();
			Log.i(WEBSERVICE_TAG, "Got response");
			Gson gson = new GsonBuilder().create();
			IndoorPositionMap p = gson.fromJson(map, IndoorPositionMap.class);
			byte[] bytes = Base64.decode(p.getDSLScript(), Base64.DEFAULT);
			String xml = new String(bytes);
			byte[] imageData = Base64.decode(p.getImageData(), Base64.DEFAULT);

			Serializer serializer = new Persister();
			ContextAwareModel model = serializer.read(ContextAwareModel.class, xml);

			EventActionAssembler eaAssembler = new EventActionAssembler(this, model);
			Collection<EventActionRule> resolvedEARules = eaAssembler.resolveSemanticModel();
			EventActionRegistry.registerAll(resolvedEARules);
			Log.d(WEBSERVICE_TAG, p.toString());
			Toast.makeText(this, "XML script found and loaded", Toast.LENGTH_LONG).show();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			Log.d(WEBSERVICE_TAG, "ERROR " + e.toString());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			Log.d(WEBSERVICE_TAG, "ERROR " + e.toString());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			Log.d("XML Persister", "ERROR " + e.toString());
		}
		
		//OnDataLoaded();
	}

	private NsdManager.DiscoveryListener mDiscoveryListener;
	private ResolveListener mResolveListener;
	private static String TAG = "NSDManager";
	private static String SERVICE_TYPE = "_http._tcp";
	private static String SERVICE_NAME = "MapServerv2";

	@SuppressLint("NewApi")
	public void initializeDiscoveryListener() {

		// Instantiate a new DiscoveryListener
		mDiscoveryListener = new NsdManager.DiscoveryListener() {

			// Called as soon as service discovery begins.
			@Override
			public void onDiscoveryStarted(String regType) {
				Log.d(TAG, "Service discovery started");
			}

			@Override
			public void onServiceFound(NsdServiceInfo service) {
				// A service was found! Do something with it.

				Log.d(TAG, "Service discovery success " + service);

				Log.d(TAG, "Service name: " + service.getServiceName());

				if (!service.getServiceType().startsWith(SERVICE_TYPE)) {
					// Service type is the string containing the protocol and
					// transport layer for this service.
					Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
					Log.d(TAG, "Was looking for Type: " + SERVICE_TYPE);
				} else if (service.getServiceName().equals(SERVICE_NAME)) {
					Log.d(TAG, "Found it! Starting resolve...");
					mNsdManager.resolveService(service, mResolveListener);
					mNsdManager.stopServiceDiscovery(this);
				}
			}

			@Override
			public void onServiceLost(NsdServiceInfo service) {
				// When the network service is no longer available.
				// Internal bookkeeping code goes here.
				Log.e(TAG, "service lost" + service);
			}

			@Override
			public void onDiscoveryStopped(String serviceType) {
				Log.i(TAG, "Discovery stopped: " + serviceType);
			}

			@Override
			public void onStartDiscoveryFailed(String serviceType, int errorCode) {
				Log.e(TAG, "Discovery failed: Error code:" + errorCode);
				mNsdManager.stopServiceDiscovery(this);
			}

			@Override
			public void onStopDiscoveryFailed(String serveType, int errorCode) {
				Log.e(TAG, "Discovery failed: Error code:" + errorCode);
				mNsdManager.stopServiceDiscovery(this);
			}

		};
	}

	public void initializeResolveListener() {
		mResolveListener = new NsdManager.ResolveListener() {

			private NsdServiceInfo mService;

			@Override
			public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
				// Called when the resolve fails. Use the error code to debug.
				Log.e(TAG, "Resolve failed for service: " + serviceInfo.getServiceName() + "Error code: " + errorCode);
			}

			@Override
			public void onServiceResolved(NsdServiceInfo serviceInfo) {
				Log.i(TAG, "Resolve Succeeded. " + serviceInfo);

				mService = serviceInfo;
				int port = mService.getPort();
				InetAddress host = mService.getHost();

				m_Port = port;
				m_Host = host;

				_waitDialog.cancel();

				CharSequence text = "Found location service (" + host.getHostAddress() + ")";
				int duration = Toast.LENGTH_LONG;

				Toast toast = Toast.makeText(TouchImageViewActivity.this, text, duration);
				toast.show();

				Log.i(TAG, "Found port: " + port);
				Log.i(TAG, "Found adress: " + host.getHostAddress());

				TouchImageViewActivity.this.InitializeWebServiceData();
			}
		};
	}

	String readFile(String fileName) throws IOException {
		ArrayList<File> list = getFilesInFolder(fileName);

		BufferedReader br = new BufferedReader(new FileReader(list.get(0)));
		try {
			StringBuilder sb = new StringBuilder();
			String line = br.readLine();

			while (line != null) {
				sb.append(line);
				sb.append("\n");
				line = br.readLine();
			}
			return sb.toString();
		} finally {
			br.close();
		}
	}

	private ArrayList<File> getFilesInFolder(String folderPath) {
		String path = Environment.getExternalStorageDirectory().toString() + folderPath;
		File folder = new File(path);

		ArrayList<File> fileList = new ArrayList<File>();
		for (File fileEntry : folder.listFiles()) {
			fileList.add(fileEntry);
		}
		return fileList;
	}

}